﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Resources;
using Ops.Mpc.ValidationServices.Attributes;
using System.ComponentModel.DataAnnotations;

namespace Ops.Mpc.ValidationServices
{
    public class ValidationManager
    {
        public static ValidationResult[] Validate(object target, params string[] propertyNames)
        {
            List<ValidationResult> validationResults = new List<ValidationResult>();
            bool retrievedData = false;
            object data = null;

            // Need to make sure you only get data from property that have a IValidationAttribute on them, and only get data once
            foreach (PropertyInfo propertyInfo in target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (propertyNames.Length > 0 && !propertyNames.Contains(propertyInfo.Name))
                    continue;

                retrievedData = false;

                foreach (Attribute attribute in propertyInfo.GetCustomAttributes(true))
                {
                    IValidationAttribute iValidationAttribute = attribute as IValidationAttribute;

                    if (iValidationAttribute != null)
                    {


                        if (!retrievedData)
                            data = propertyInfo.GetValue(target, new object[0]);


                        if (!iValidationAttribute.Validate(target, data, propertyNames))
                        {
                            DisplayAttribute[] displayAttributes = (DisplayAttribute[])propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), false);
                            string name = string.Empty;

                            if (displayAttributes != null && displayAttributes.Length > 0)
                                name = displayAttributes[0].Name;
                            else
                                name = propertyInfo.Name;

                            Assembly assembly = propertyInfo.DeclaringType.Assembly;

                            string message = "Validation error";

                            

                            try
                            {
                                if(iValidationAttribute.Message != null)
                                     message = iValidationAttribute.Message;
                                else
                                    message = new ResourceManager(string.Format("{0}.ValidationMessages", assembly.FullName.Split(new char[] { ',' })[0]), assembly).GetString(iValidationAttribute.MessageId);
                            }
                            catch { }

                            validationResults.Add(new ValidationResult(target, propertyInfo.Name, iValidationAttribute.FormatMessage(message,target,data,  name)));
                        }
                    }
                }
            }

            // Do the buniness validators too
            foreach (Attribute attribute in target.GetType().GetCustomAttributes(true))
            {
                IValidationAttribute iValidationAttribute = attribute as IValidationAttribute;

                if (iValidationAttribute != null)
                {
                    if (!iValidationAttribute.Validate(target, target, propertyNames))
                    {
                        Assembly assembly = target.GetType().Assembly;
                        DisplayAttribute[] displayAttributes = (DisplayAttribute[])target.GetType().GetCustomAttributes(typeof(DisplayAttribute), false);
                        string name = string.Empty;

                        if (displayAttributes != null && displayAttributes.Length > 0)
                            name = displayAttributes[0].Name;
                        else
                            name = target.GetType().Name;
                        
                        string message = "Validation error";

                        try
                        {
                            if (iValidationAttribute.Message != null)
                                message = iValidationAttribute.Message;
                            else
                                message = new ResourceManager(string.Format("{0}.ValidationMessages", assembly.FullName.Split(new char[] { ',' })[0]), assembly).GetString(iValidationAttribute.MessageId);
                        }
                        catch { }

                        validationResults.Add(new ValidationResult(target, string.Empty, iValidationAttribute.FormatMessage(message, target, propertyNames, name)));
                    }
                }

            }


            return validationResults.ToArray();

        }

        public static void ValidateValidationMessages(Type type)
        {
            Assembly assembly = type.Assembly;
            ResourceManager resourceManager = new ResourceManager(string.Format("{0}.ValidationMessages", assembly.FullName), assembly);

            // Need to make sure you only get data from property that have a IValidationAttribute on them, and only get data once
            foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                foreach (Attribute attribute in propertyInfo.GetCustomAttributes(true))
                {
                    IValidationAttribute iValidationAttribute = attribute as IValidationAttribute;

                    if (iValidationAttribute != null)
                        if (string.IsNullOrEmpty(resourceManager.GetString(iValidationAttribute.MessageId)))
                            throw new Exception(string.Format("Type: {0}, Property: {1}, defines attribute of Type: {2} which uses unknown Message Id: {3}", type.FullName, propertyInfo.Name, iValidationAttribute.GetType().FullName, iValidationAttribute.MessageId));

                }
            }

            // Do the buniness validators too
            foreach (Attribute attribute in type.GetType().GetCustomAttributes(true))
            {
                IValidationAttribute iValidationAttribute = attribute as IValidationAttribute;

                if (iValidationAttribute != null)
                    if (string.IsNullOrEmpty(resourceManager.GetString(iValidationAttribute.MessageId)))
                        throw new Exception(string.Format("Type: {0}, defines attribute of Type: {1} which uses unknown Message Id: {2}", type.FullName, iValidationAttribute.GetType().FullName, iValidationAttribute.MessageId));

            }

        }
    }
}
